#include "evm_process.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

evm_process* evm_process_new      ( struct evm* vm, const char *name ) {
    evm_process *p = malloc( sizeof( evm_process ) );

    if ( p ) {
        p->args_stack   = 0;
        p->call_stack   = 0;
        p->data_stack   = 0;
        p->memory       = 0;
        p->pc           = 0;
        p->process_name = 0;
        p->state        = INIT;
        p->vm           = 0;

        // initialize args stack
        p->args_stack = evm_stack_new( EVM_BASE_ARGS_STACK_SIZE );
        if ( !p->args_stack ) {
            evm_process_free( p );
            return 0; //ERR_MEMALLOC_FAILED;
        }

        // initialize call stack
        p->call_stack = evm_stack_new( EVM_BASE_CALL_STACK_SIZE );
        if ( !p->call_stack ) {
            evm_process_free( p );
            return 0; //ERR_MEMALLOC_FAILED;
        }

        // initialize data stack
        p->data_stack = evm_stack_new( EVM_BASE_DATA_STACK_SIZE );
        if ( !p->data_stack ) {
            evm_process_free( p );
            return 0; //ERR_MEMALLOC_FAILED;
        }

        // initialize memory
        evm_mem_initialize( &p->memory );
        if ( !p->memory ) {
            evm_process_free( p );
            return 0; //ERR_MEMALLOC_FAILED;
        }

        // initialize process name
        p->process_name = malloc( strlen( name ) + 1 );
        if ( p->process_name ) {
            strcpy( p->process_name, name );
        } else {
            evm_process_free( p );
            return 0; //
        }

        // initialize pc
        p->pc = (evm_command*)p->memory->index.ptrs[0];


    } else {
        return 0; //ERR_MEMALLOC_FAILED;
    }

    return p;
}


evm_process* evm_process_from_file( struct evm* vm, const char *name, const char *file_name ) {
    uint32_t        addr   = 0;
    const uint32_t  bsize  = 1024;
    uint8_t        *buffer = 0;
    uint32_t        rsize  = 0;
    evm_process    *p      = evm_process_new( vm, name );

    if ( !p ) {
        return 0;
    }

    FILE *f = fopen( file_name, "r" );

    if ( !f ) {
        evm_process_free( p );
        return 0;
    }

    buffer = malloc( bsize );
    if ( !buffer ) {
        fclose( f );
        evm_process_free( p );
        return 0;
    }

    do {
        rsize = fread( buffer, bsize, 1, f );
        if ( rsize > 0 ) {
            evm_mem_write( p->memory, addr, buffer, rsize );
            addr += rsize;
        }
    } while ( rsize > 0 );

    fclose( f );

    return p;
}


void evm_process_free ( evm_process *proc ) {
    if ( proc ) {
        if ( proc->args_stack )
            evm_stack_free( proc->args_stack );

        if ( proc->call_stack )
            evm_stack_free( proc->call_stack );

        if ( proc->data_stack )
            evm_stack_free( proc->data_stack );

        if ( proc->memory )
            evm_mem_free( proc->memory, 0 );

        if ( proc->process_name )
            free( proc->process_name );

        free( proc );
    } else {

    }
}


uint32_t evm_arg  ( evm_process *proc, int32_t argNo, void *data ) {

}


uint32_t evm_stack_data ( evm_process *proc, int32_t stackLevel, void *data ) {
    if ( -stackLevel > proc->data_stack->size ) {
        return ERR_STACK_INDEXING_ERROR;
    }
    *((uint32_t*)data) = *(proc->data_stack->sp + stackLevel);
    return EVM_OK;
}
